package org.orr.budgetpro.services;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.orr.budgetpro.entities.Budget;
import org.orr.budgetpro.entities.BudgetItem;
import org.orr.budgetpro.entities.ExpenseCategory;
import org.orr.budgetpro.entities.ExpenseItem;
import org.orr.budgetpro.entities.IncomeItem;
import org.orr.budgetpro.report.ExpenseCategoryTotal;
import org.springframework.stereotype.Service;

@Service
public class BudgetCalculator {

	/**
	 * Returns the sum total of all IncomeItems in the given budget. Null
	 * IncomeItems and amounts are treated as 0.
	 * 
	 * @param budget
	 *            Budget to perform calculations on
	 * @return sum total of all IncomeItems
	 * @throws IllegalArgumentException
	 *             if budget is null
	 */
	public Integer getIncomeSubtotal(Budget budget)
			throws IllegalArgumentException {
		if (budget == null)
			throw new IllegalArgumentException("Budget must not be null");

		List<IncomeItem> incomeItems = budget.getIncomeItems();

		return getItemSubtotal(incomeItems);
	}

	/**
	 * Returns the sum total of all ExpenseItems in the given budget. Null
	 * ExpenseItems and amounts are treated as 0.
	 * 
	 * @param budget
	 *            Budget to perform calculations on
	 * @return sum total of all ExpenseItems
	 * @throws IllegalArgumentException
	 *             if budget is null
	 */
	public Integer getExpenseSubtotal(Budget budget)
			throws IllegalArgumentException {
		if (budget == null)
			throw new IllegalArgumentException("Budget must not be null");

		List<ExpenseItem> expenseItems = budget.getExpenseItems();

		return getItemSubtotal(expenseItems);
	}

	private Integer getItemSubtotal(List<? extends BudgetItem> items) {
		if (items == null)
			return 0;

		Integer total = 0;
		for (BudgetItem item : items) {
			if (item != null && item.getAmount() != null)
				total += item.getAmount();
		}

		return total;
	}

	public List<ExpenseCategoryTotal> getExpenseCategoryTotals(Budget budget) {
		// First, build a map with category as the key, and a list of
		// corresponding expense items as the value
		Map<ExpenseCategory, List<ExpenseItem>> categoryItemMap = new HashMap<ExpenseCategory, List<ExpenseItem>>();

		List<ExpenseItem> expenseItems = budget.getExpenseItems();
		for (ExpenseItem expenseItem : expenseItems) {
			List<ExpenseItem> catItems = categoryItemMap.get(expenseItem
					.getExpenseCategory());
			if (catItems == null) {
				catItems = new ArrayList<ExpenseItem>();
				categoryItemMap.put(expenseItem.getExpenseCategory(), catItems);
			}
			catItems.add(expenseItem);
		}

		// Now create a ExpenseCategoryTotal for each category
		List<ExpenseCategoryTotal> totals = new ArrayList<ExpenseCategoryTotal>();
		Set<Entry<ExpenseCategory, List<ExpenseItem>>> categoryItemEntries = categoryItemMap
				.entrySet();

		for (Entry<ExpenseCategory, List<ExpenseItem>> entry : categoryItemEntries) {
			ExpenseCategory currentCategory = entry.getKey();
			List<ExpenseItem> currentItems = entry.getValue();

			// Get total for the items
			Integer itemTotal = this.getItemSubtotal(currentItems);

			ExpenseCategoryTotal total = new ExpenseCategoryTotal(
					currentCategory, itemTotal, currentItems);
			totals.add(total);
		}

		return totals;
	}
	
	public Integer getNetBudgetTotal(Budget budget) {
		Integer incomeSubtotal = getIncomeSubtotal(budget);
		Integer expenseSubtotal = getExpenseSubtotal(budget);
		
		return incomeSubtotal - expenseSubtotal;
	}
}
